<?php

namespace app\service;

use app\model\WechatLogDao;
use think\admin\storage\LocalStorage;
use think\facade\Request;

/**
 * 微信小程序
 */
class Weixin
{

    private function send(string $method, string $url, ?string $data = null): array
    {
        $start = microtime(true);
        $log = new WechatLogDao();
        $log->save([
            'url' => $url,
            'request' => $data,
            'ip' => Request::ip(),
            'uid' => request()->uid,
        ]);
        if (strtoupper($method) === 'POST') {
            $ret = curl_post($url, $data);
        } else {
            $ret = curl_get($url);
        }
        $time = (microtime(true) - $start) * 1000;
        $log->save([
            'response' => $ret,
            'time' => $time,
        ]);
        return [$ret, $log->id];
    }


    public function getOpenid(string $code)
    {
        $url = "https://api.weixin.qq.com/sns/jscode2session?appid=" . config('wechat.app_id') . "&secret=" . config('wechat.secret') . "&js_code=" . $code . "&grant_type=authorization_code";
        //$res = http_get($url);
        list($res, $logId) = $this->send('GET', $url, null);
        return json_decode($res, true);
    }

    public function getAccessToken(): string
    {
        $appid = config('wechat.app_id');
        $key = $appid . ':access_token';
        $accessToken = cache($key);
        if (!empty($accessToken)) {
            return $accessToken;
        }
        $secret = config('wechat.secret');
        $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . $appid . '&secret=' . $secret;
        list($res, $logId) = $this->send('GET', $url);
        $res = json_decode($res, true);
        if (!empty($res['access_token'])) {
            $accessToken = $res['access_token'];
            cache($key, $accessToken, 7000);
            return $accessToken;
        }
        show_error(!empty($res['errmsg']) ? $res['errmsg'] : 'access_token获取失败');
    }


    /**
     * 获取不限制的小程序码
     * @param $id
     * @return array|mixed
     * @link https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/qr-code/getUnlimitedQRCode.html
     */
    public function getQrCode($id, $uid, $page = 'pages/detail/detail')
    {
        $postData['scene'] = 'id=' . $id . '&uid=' . $uid;
        $postData['page'] = $page;
        $postData['check_path'] = false;
        $postData['env_version'] = 'release';
        $token = $this->getAccessToken();
        $url = 'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=' . $token;
        //$info = http_post($url, $postData);
        $info = curl_post($url, json_encode($postData, JSON_UNESCAPED_UNICODE));
        $bytes = substr($info, 0, 2);
        $head = @unpack('C2char', $bytes);
        if (is_array($head) && count($head) == 2) {
            if ($head['char1'] == 255 && $head['char2'] == 216) {//JPG
                $fileName = sprintf("qrcode/%d.png", $id);
                // 上传文件
                return LocalStorage::instance()->set($fileName, $info);
            }
        }
        $ret = json_decode($info, true);
        if (isset($ret['errcode']) && $ret['errcode'] == 40001) {
            $key = "access_token";
            cache($key, null);//删除
        }
        return $ret;
    }

    public function getMobile($code)
    {
        $token = $this->getAccessToken();
        $url = 'https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=' . $token;
        $postData = [
            'code' => $code
        ];
        $res = curl_post($url, json_encode($postData, JSON_UNESCAPED_UNICODE));
        $ret = json_decode($res, true);
        $phone = $ret['phone_info']['phoneNumber'] ?? '';
        if (empty($phone)) {
            sysoplog('mobile', $res);
            show_error('请求微信手机号失败！');
        }
        return $phone;
    }

    //--------------------------------公共方法----------------------------------------------
    //生产随机字符串
    public static function randomkeys($length = 16)
    {
        $returnStr = '';
        $pattern = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        for ($i = 0; $i < $length; $i++) {
            $idx = mt_rand(0, 61);
            $returnStr .= $pattern[$idx];
        }
        return $returnStr;
    }

    //生产签名
    public static function MakeSign($data, $mchKey = '', $algo = 'MD5')
    {
        $apikey = $mchKey;
        //签名步骤一：按字典序排序参数
        ksort($data);
        $string = self::ToUrlParams($data);
        //签名步骤二：在string后加入KEY
        $string = $string . "&key=" . $apikey;
        //签名步骤三：MD5加密
        if (strtolower($algo) == 'md5') {
            $string = md5($string);
        } else {
            //$string=hash_hmac('sha256', $string, $apikey);
            $string = hash_hmac($algo, $string, $apikey);
        }
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    public static function ToUrlParams($arr)
    {
        $buff = "";
        foreach ($arr as $k => $v) {
            if ($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
        }
        return trim($buff, "&");
    }

    public static function ToXml($arr)
    {
        if (!is_array($arr) || count($arr) <= 0) {
            show_error("数组数据异常");
        }
        $xml = "<xml>";
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    /**
     * 将xml转为array
     * @param string $xml
     */
    public static function FromXml($xml)
    {
        if (!$xml) {
            show_error("xml数据异常！");
        }
        //将XML转为array
        //禁止引用外部xml实体
        //if (\PHP_VERSION_ID < 80000) {
        //    libxml_disable_entity_loader(true);
        //}
        $vale = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $vale;
    }


}